package cn.chenfeifx.core.controls;

import cn.chenfeifx.core.common.TimelineBuilder;
import cn.chenfeifx.core.layout.PopoverNode;
import javafx.animation.Timeline;
import javafx.beans.property.BooleanProperty;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.SimpleBooleanProperty;
import javafx.beans.property.SimpleObjectProperty;
import javafx.geometry.Bounds;
import javafx.geometry.Rectangle2D;
import javafx.scene.Node;
import javafx.scene.effect.DropShadow;
import javafx.scene.layout.Region;
import javafx.scene.paint.Color;
import javafx.scene.paint.Paint;
import javafx.stage.Popup;
import javafx.stage.Screen;
import javafx.stage.Window;
import javafx.util.Duration;

import java.util.HashMap;
import java.util.Map;
import java.util.Optional;

/**
 * 气泡卡片
 */
public class CFPopover {

    private static final Map<CFPopover.Pos, CFPopover.Pos> OPPOSITE = new HashMap<>();

    static {
        //初始化相反位置
        OPPOSITE.put(CFPopover.Pos.BOTTOM_LEFT, CFPopover.Pos.TOP_LEFT);
        OPPOSITE.put(CFPopover.Pos.BOTTOM_CENTER, CFPopover.Pos.TOP_CENTER);
        OPPOSITE.put(CFPopover.Pos.BOTTOM_RIGHT, CFPopover.Pos.TOP_RIGHT);
        OPPOSITE.put(CFPopover.Pos.LEFT_TOP, CFPopover.Pos.RIGHT_TOP);
        OPPOSITE.put(CFPopover.Pos.LEFT_CENTER, CFPopover.Pos.RIGHT_CENTER);
        OPPOSITE.put(CFPopover.Pos.LEFT_BOTTOM, CFPopover.Pos.RIGHT_BOTTOM);
        //相反方向遍历重新加一遍
        Map<CFPopover.Pos, CFPopover.Pos> putMap = new HashMap<>();
        OPPOSITE.forEach((k, v) -> putMap.put(v, k));
        OPPOSITE.putAll(putMap);
    }

    private final PopoverNode popoverNode;
    private final PopoverWindow popoverWindow;

    public CFPopover(Node owner, Region content) {
        setOwner(owner);
        popoverNode = new PopoverNode(content);
        popoverNode.setEffect(new DropShadow(5, 0, 0, Color.rgb(0, 0, 0, 0.12))); // shadow
        popoverWindow = new PopoverWindow(popoverNode);
    }

    private Timeline showAnimation;
    private Timeline hideAnimation;

    /**
     * 显示气泡卡片
     */
    public void show() {
        popoverWindow.show(getOwner().getScene().getWindow());
    }

    // owner
    private final ObjectProperty<Node> owner = new SimpleObjectProperty<>();

    public Node getOwner() {
        return owner.get();
    }

    public ObjectProperty<Node> ownerProperty() {
        return owner;
    }

    public void setOwner(Node owner) {
        this.owner.set(owner);
    }

    // Pos
    private final ObjectProperty<Pos> pos = new SimpleObjectProperty<>(Pos.BOTTOM_CENTER);

    public Pos getPos() {
        return pos.get();
    }

    public ObjectProperty<Pos> posProperty() {
        return pos;
    }

    public void setPos(Pos pos) {
        this.pos.set(pos);
    }

    // 是否自动调整位置
    private final BooleanProperty autoFix = new SimpleBooleanProperty(true);

    public boolean isAutoFix() {
        return autoFix.get();
    }

    public BooleanProperty autoFixProperty() {
        return autoFix;
    }

    public void setAutoFix(boolean autoFix) {
        this.autoFix.set(autoFix);
    }

    /**
     * 隐藏
     */
    public void hide() {
        if (popoverWindow.isShowing()) {
            popoverWindow.hide();
        }
    }

    public PopoverWindow getPopoverWindow() {
        return popoverWindow;
    }

    public PopoverNode getPopoverNode() {
        return popoverNode;
    }

    public void setFill(Paint paint) {
        popoverNode.setStroke(Color.TRANSPARENT);
        popoverNode.setFill(paint);
    }

    /**
     * 弹出窗口
     */
    public class PopoverWindow extends Popup {

        PopoverWindow(Node content) {
            setAutoFix(false);
            setAutoHide(true);
            setOpacity(0);
            getContent().setAll(content);
            initialize();
        }

        private void initialize() {
            // 位置监听设置
            popoverNode.layoutBoundsProperty().addListener((observable, oldValue, newValue) -> setPosition());
            // 监听所属窗口
            ownerWindowProperty()
                    .addListener((observable, oldValue, newValue) ->
                            Optional.ofNullable(newValue).ifPresent(window -> {
                                window.setOnHiding(event -> super.hide()); // 所属窗口关闭，直接关闭。
                            }));
            // Popup显示逻辑
            setOnShown(event -> {
                setPosition();
                // 动画显示
                if (showAnimation == null) {
                    showAnimation = TimelineBuilder.builder()
                            .addKeyFrame(opacityProperty(), 0, Duration.ZERO)
                            .addKeyFrame(opacityProperty(), 1, Duration.millis(100))
                            .build();
                }
                showAnimation.play();
            });
        }

        /**
         * 设置位置
         */
        private void setPosition() {
            // 获取ownerNode在屏幕中的位置
            Bounds bounds = getOwner().localToScreen(getOwner().getLayoutBounds()); // ownerNode在屏幕中的位置
            Bounds toScene = popoverNode.localToScene(popoverNode.getLayoutBounds()); // popoverNode在场景中的位置
            Pos pos = getPos();
            if (CFPopover.this.isAutoFix()) {
                // 计算要显示的位置是否超出屏幕
                Rectangle2D visualBounds = Screen.getPrimary().getVisualBounds();
                boolean reset = false;
                switch (pos) {
                    case BOTTOM_LEFT, BOTTOM_CENTER, BOTTOM_RIGHT ->
                            reset = visualBounds.getMaxY() < getHeight() + bounds.getMaxY();
                    case TOP_LEFT, TOP_CENTER, TOP_RIGHT -> reset = getHeight() > bounds.getMinY();
                    case RIGHT_TOP, RIGHT_CENTER, RIGHT_BOTTOM ->
                            reset = visualBounds.getMaxX() < getWidth() + bounds.getMaxX();
                    case LEFT_TOP, LEFT_CENTER, LEFT_BOTTOM -> reset = getWidth() > bounds.getMinX();
                }
                if (reset) {
                    pos = OPPOSITE.get(pos);
                }
            }
            // 计算窗口要显示的位置
            popoverNode.setOffset(-1);
            switch (pos) {
                case BOTTOM_CENTER -> {
                    popoverNode.setTrianglePos(PopoverNode.Pos.TOP);
                    setX(bounds.getCenterX() - getWidth() / 2);
                    setY(bounds.getMaxY());
                }
                case TOP_CENTER -> {
                    popoverNode.setTrianglePos(PopoverNode.Pos.BOTTOM);
                    setX(bounds.getCenterX() - getWidth() / 2);
                    setY(bounds.getMinY() - getHeight());
                }
                case LEFT_CENTER -> {
                    popoverNode.setTrianglePos(PopoverNode.Pos.RIGHT);
                    setX(bounds.getMinX() - getWidth());
                    setY(bounds.getCenterY() - getHeight() / 2);
                }
                case RIGHT_CENTER -> {
                    popoverNode.setTrianglePos(PopoverNode.Pos.LEFT);
                    setX(bounds.getMaxX());
                    setY(bounds.getCenterY() - getHeight() / 2);
                }
                case BOTTOM_LEFT -> { // 下左
                    popoverNode.setTrianglePos(PopoverNode.Pos.TOP);
                    popoverNode.setOffset(bounds.getWidth() / 2);
                    setX(bounds.getMinX() - toScene.getMinX());
                    setY(bounds.getMaxY());
                }
                case BOTTOM_RIGHT -> { // 下右
                    popoverNode.setTrianglePos(PopoverNode.Pos.TOP);
                    popoverNode.setOffset(toScene.getWidth() - bounds.getWidth() / 2);
                    setX(bounds.getMaxX() - getWidth() + (getWidth() - toScene.getMaxX()));
                    setY(bounds.getMaxY());
                }
                case TOP_LEFT -> { // 上左
                    popoverNode.setTrianglePos(PopoverNode.Pos.BOTTOM);
                    popoverNode.setOffset(bounds.getWidth() / 2);
                    setX(bounds.getMinX() - toScene.getMinX());
                    setY(bounds.getMinY() - getHeight());
                }
                case TOP_RIGHT -> { // 上右
                    popoverNode.setTrianglePos(PopoverNode.Pos.BOTTOM);
                    popoverNode.setOffset(toScene.getWidth() - bounds.getWidth() / 2);
                    setX(bounds.getMaxX() - getWidth() + (getWidth() - toScene.getMaxX()));
                    setY(bounds.getMinY() - getHeight());
                }
                case LEFT_TOP -> { // 左上
                    popoverNode.setTrianglePos(PopoverNode.Pos.RIGHT);
                    popoverNode.setOffset(bounds.getHeight() / 2);
                    setX(bounds.getMinX() - getWidth());
                    setY(bounds.getMinY() - toScene.getMinY());
                }
                case LEFT_BOTTOM -> { // 左下
                    popoverNode.setTrianglePos(PopoverNode.Pos.RIGHT);
                    popoverNode.setOffset(toScene.getHeight() - bounds.getHeight() / 2);
                    setX(bounds.getMinX() - getWidth());
                    setY(bounds.getMaxY() - getHeight() + toScene.getMinY());
                }
                case RIGHT_TOP -> { // 右上
                    popoverNode.setTrianglePos(PopoverNode.Pos.LEFT);
                    popoverNode.setOffset(bounds.getHeight() / 2);
                    setX(bounds.getMaxX());
                    setY(bounds.getMinY() - toScene.getMinY());
                }
                case RIGHT_BOTTOM -> { // 右下
                    popoverNode.setTrianglePos(PopoverNode.Pos.LEFT);
                    popoverNode.setOffset(toScene.getHeight() - bounds.getHeight() / 2);
                    setX(bounds.getMaxX());
                    setY(bounds.getMaxY() - getHeight() + toScene.getMinY());
                }
            }
        }


        @Override
        public void show(Window owner) {
            super.show(owner);
        }

        /**
         * Popup动画隐藏
         */
        @Override
        public void hide() {
            if (hideAnimation == null) {
                hideAnimation = TimelineBuilder.builder()
                        .delay(Duration.millis(200))
                        .addKeyFrame(opacityProperty(), 1, Duration.ZERO)
                        .addKeyFrame(opacityProperty(), 0, Duration.millis(100))
                        .onFinished(event -> super.hide())
                        .build();
            }
            hideAnimation.play();
        }
    }

    /**
     * 位置
     */
    public enum Pos {
        BOTTOM_LEFT, BOTTOM_CENTER, BOTTOM_RIGHT,
        TOP_LEFT, TOP_CENTER, TOP_RIGHT,
        RIGHT_TOP, RIGHT_CENTER, RIGHT_BOTTOM,
        LEFT_TOP, LEFT_CENTER, LEFT_BOTTOM
    }

}